﻿package com.cilico.single.view;

import java.util.Vector;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import com.cilico.single.ConfigManager;
import com.cilico.single.components.ChannelParam;
import com.cilico.single.components.ChannelRepository;
import com.cilico.single.components.POI;
import com.cilico.single.components.Paging;
import com.cilico.single.utils.Utils;

/**
 * Completel defined property of one channel. We save the values (as strings) of the last query in values_. XXX FIXME: I
 * don't understand this but this class has to be public for it to compile.
 */
public final class Channel {
	/**
	 * This gets thrown when we cannot load anything from a channel at the current location.
	 */
	@SuppressWarnings("serial")
	class EmptyChannelException extends Exception {
		EmptyChannelException(final String str) {
			super(str);
		}
	}

	private String workspace_;
	private String title_;
	private String url_;
	private String id_;
	public String shortCut_;
	private int userid_;
	public String logoUrl_;
	private String channelUrl_;
	private String channelSUrl_;
	private String channelFunId_;
	private String channelName_;
	private String iconName_;

	public int total_;
	int showNew_;
	
	/**
	 * The senior channel id of the channel which is selected.It is only used by channel history.
	 */
	private String seniorChannelID_;

	private Vector<ChannelParam> params_;
	public String queryString_;

	public static final int MAX_HISTORY = 4;
	private final String[] history_;

	/** For display on map status bar. */
	private String shortTitle_;

	/** Index of this channel in ChannelRepository. */
	private int index_;

	/** Saves all known POIs of this channel */
	public Vector<POI> poiList_;

	/** Paging control for this channel. */
	public final Paging paging_;

	public Bitmap titleImg_;
	Bitmap iconImg_;

	/**
	 * Sorting criteria for this channel. XXX Save this to RMS.
	 */
	private String sorting_;

	public boolean isCollection_; // 一个二级菜单节点（Collection）

	/**
	 * NOTE: This must match the feed type defined on the server side. See lp-api.pdf.
	 */
	public static final int FEED_TYPE_APP = 1;
	public static final int FEED_TYPE_ATOM = 2;
	public static final int FEED_TYPE_FAVORITE = 3;
	public static final int FEED_TYPE_CALENDAR = 4;
	public static final int FEED_TYPE_SMS = 5;
	private int feedType_;

	/**
	 * These are used to tell if a channel is loaded from the device's PIM, such as calendar. We use this option to put
	 * device-local calendars in the calendar collection.
	 */
	public static final int FEED_SOURCE_WEB = 0;
	public static final int FEED_SOURCE_DEVICE = 1;
	private int feedSource_;

	// XXX This should switch to enum in Java 5.
	public static final int APP = 1;
	public static final int FEED = 2;
	public static final int SMS = 3;

	// may be use by channel action.
	public String userName_;
	public String passWord_;

	public Channel(ChannelRepository channelRepo) {

		params_ = new Vector<ChannelParam>();
		index_ = 0;
		shortTitle_ = null;
		userid_ = -1;
		logoUrl_ = null;
		feedSource_ = FEED_SOURCE_WEB;
		poiList_ = new Vector<POI>();

		// Keep track and display up to 4 recent query histories.
		history_ = new String[MAX_HISTORY];

		paging_ = new Paging();
	}

	public boolean isPredefined() {
		return feedSource_ == FEED_SOURCE_DEVICE;
	}

	public String title() {
		return title_;
	}

	public void setTitle(String title) {
		title_ = title;
	}

	public String shortTitle() {
		return shortTitle_;
	}

	public void setShortTitle(String title) {
		shortTitle_ = title;
	}

	/**
	 * This function helps caller decide the basic characteristics of this channel without having to know every feed
	 * type.
	 */
	public int type() {
		switch (feedType_) {
		case FEED_TYPE_FAVORITE:
		case FEED_TYPE_ATOM:
			return FEED;
		case FEED_TYPE_SMS:
			return SMS;
		case FEED_TYPE_APP:
		case FEED_TYPE_CALENDAR:
		default:
			return APP;
		}
	}

	public int feedType() {
		return feedType_;
	}

	public int feedSource() {
		return feedSource_;
	}
	
	public int total() {

		return total_;

	}

	public int getShowNew() {

		return showNew_;

	}

	public String id() {
		return id_;
	}
	
	public void setTotal(int total) {

		total_ = total;

	}

	public void setShowNew(int showNew) {

		showNew_ = showNew;

	}

	public void setId(String id) {
		id_ = id;
	}

	public int userid() {
		return userid_;
	}

	public void setUserid(String userInfo) {
		userid_ = Integer.parseInt(userInfo);
	}

	public String seniorID() {
		return seniorChannelID_;
	}

	public void setSeniorID(String seniroid) {
		seniorChannelID_ = seniroid;
	}

	public String workspace() {
		return workspace_;
	}

	public void setWorkspace(String workspace) {
		workspace_ = workspace;
	}

	public String url() {
		return url_;
	}

	public void setUrl(String url) {
		url_ = url;
	}

	public void setFeedType(String type) {
		feedType_ = Integer.parseInt(type);
	}

	void setFeedType(int t) {
		feedType_ = t;
	}

	void setFeedSource(int s) {
		feedSource_ = s;
	}

	public int index() {
		return index_;
	}

	public void setIndex(int i) {
		index_ = i;
	}

	Vector<ChannelParam> params() {
		return params_;
	}

	public int numParams() {
		return params_.size();
	}

	public void addParam(ChannelParam p) {
		params_.addElement(p);
	}

	/**
	 * Assemble query parameters from params_ and saved values_.
	 * 
	 * NOTE: current page number is passed into the query string by PoiRepository but not stored in this pre-built query
	 * string.
	 */
	void genQueryString() {
		StringBuffer buf = new StringBuffer();
		int num = numParams();
		for (int i = 0; i < num; ++i) {
			ChannelParam par = (ChannelParam) params_.elementAt(i);
			if (par.type_.equals("label") || (par.type_.equals("radio") && !par.isChecked_))
				continue;
			buf.append("&");
			buf.append(par.name_);
			buf.append("=");
			if (par.type_.equals("checkbox") && !par.isChecked_)
				continue;
			if (par.value_ != null)
				buf.append(Utils.escapeURIComponent(par.value_.trim()));
		}
		buf.deleteCharAt(0); // delete the first '&'
		queryString_ = buf.toString();
		if (params_.size() == 1)
			addQueryHistory(queryString_);
	}

	/**
	 * Return true if this channel needs to be downloaded again when we move to a new location. XXX FIXME This should
	 * eventually be a server-side property of the channel.
	 */
	public boolean updateAtNewLocation() {
		switch (feedType_) {
		case FEED_TYPE_SMS:
			return false;
		case FEED_TYPE_FAVORITE: // Need to update user's addresses at a new
									// place
		case FEED_TYPE_CALENDAR:
		case FEED_TYPE_APP:
		case FEED_TYPE_ATOM:
		default:
			return true;
		}
	}

	public void clear() {
		poiList_.removeAllElements();
	}

	public int size() {
		return poiList_.size();
	}

	public boolean empty() {
		return (poiList_.size() == 0);
	}

	public void addPOI(POI poi) {
		poiList_.addElement(poi);
	}

	/**
	 * Given one of strings of a POI's attribute names, sort all existing POIs based on that attribute. NOTE: sorting is
	 * defined and kept on a per-channel basis. Because different channels may have different fields for sorting, we
	 * cannot apply sorting criteria of one channel to another one.
	 */
	public void sortPOIs(String string) throws Exception {
		Vector<POI> origPois = poiList_;
		poiList_ = new Vector<POI>();
		sorting_ = (string == null) ? getDefaultSorting() : string;
		populate(origPois);
	}

	private String getDefaultSorting() {
		// XXX FIXME: if this is a calendar, sort by time.
		// This should be driven by channel property instead of pre-defined.
		if (feedType_ == Channel.FEED_TYPE_CALENDAR)
			return "Time";
		else
			return "Distance";
	}

	public String getSorting() {
		return (sorting_ == null) ? getDefaultSorting() : sorting_;
	}

	public void setSorting(String str) {
		sorting_ = str;
	}

	/**
	 * Insert a new POI to the current list. Update paging if we have only one page. (If we have > 1, we'll update
	 * paging when we load the second page.)
	 */
	public void insert(POI poi) throws Exception {
		String sortField = getSorting();

		if (poiList_.isEmpty()) {
			poiList_.addElement(poi);
		} else {
			int sz = poiList_.size();
			int i = 0;
			for (; i < sz; i++) {
				POI dest = (POI) poiList_.elementAt(i);
				if (poi.compareBy(dest, sortField) <= 0) {
					poiList_.insertElementAt(poi, i);
					break;
				}
			}
			if (i >= sz && i < ConfigManager.POIS_PER_SCREEN) {
				poiList_.addElement(poi);
			} else if (i == ConfigManager.POIS_PER_SCREEN) {
				poiList_.removeElementAt(ConfigManager.POIS_PER_SCREEN - 1);
				poiList_.insertElementAt(poi, 0);
			}
		}
	}

	/**
	 * Update paging after a new POI is inserted.
	 */
	void updateByNewPoi() {
		if (paging_.atEnd() && poiList_.size() > paging_.pageSize())
			paging_.setLastPage(paging_.lastPage_ + 1);
	}

	/**
	 * Populate internal POI list, sorted in the order of distance to current location (as dictated by the map).
	 */
	public void populate(Vector<POI> poiList) throws Exception {
		poiList_.removeAllElements();
		if (poiList == null || poiList.isEmpty()) {
			throw (new EmptyChannelException("没有相关内容."));
		}
		// Sort the poi by the distance to the current center of the map.
		// NOTE: we should not sort by current location in LocationManager,
		// because
		// user may pan the map and look and query based on that map center.
		// FIXME: Need optimization if we begin to have a lot of pois.
		String sortField = getSorting();
		poiList_.addElement(poiList.elementAt(0));
		int plSize = poiList.size();
		for (int i = 1; i < plSize; ++i) {
			POI p = (POI) poiList.elementAt(i);
			int j;
			int pvSize = poiList_.size();
			for (j = 0; j < pvSize; ++j) {
				POI q = (POI) poiList_.elementAt(j);
				if (p.compareBy(q, sortField) < 0) {
					poiList_.insertElementAt(p, j);

					Utils.printOutToConsole("inserted " + p.name() + " at " + j);
					break;
				}
			}
			if (j >= pvSize) {
				poiList_.addElement(p);
			}
		}
	}

	/**
	 * When a channel has a list of previous queries, remember its past query history.
	 */
	void addQueryHistory(String string) {
		// if this query is already exist in history, we will delete it from
		// history
		int sz = getQueryHistorySize();
		for (int i = 0; i < sz; ++i) {
			if (history_[i].equals(string)) {
				for (int j = i; j < sz - 1; ++j)
					history_[j] = history_[j + 1];
				history_[sz - 1] = null;
				break;
			}
		}
		// Shift current query history up by 1, and insert this at the top.
		for (int i = MAX_HISTORY - 2; i >= 0; --i) {
			if (history_[i] == null)
				continue;
			history_[i + 1] = history_[i];
		}
		history_[0] = string;

		Utils.printOutToConsole("channel " + title_ + " added history " + string + " size " + getQueryHistorySize());
	}

	/**
	 * When we load query history from a channel XML, insert them in the order of appearance.
	 */
	public void initAddQueryHistory(String string) {
		for (int i = 0; i < MAX_HISTORY; ++i) {
			if (history_[i] == null) {
				history_[i] = string;
				break;
			}
		}
	}

	int getQueryHistorySize() {
		for (int i = 0; i < history_.length; ++i)
			if (history_[i] == null)
				return i;
		return history_.length;
	}

	String getQueryHistory(int i) {
		return history_[i];
	}

	void setQueryFromHistory(int i) {
		String tmp = history_[i];
		queryString_ = tmp;
		history_[i] = history_[0];
		history_[0] = tmp;
	}

	void resetPaging() {
		paging_.setCurrentPage(1);
	}

	void setIconImage(byte[] str) {
		iconImg_ = BitmapFactory.decodeByteArray(str, 0, str.length);
	}

	boolean isQueryChannel() {
		if (type() == Channel.APP && numParams() > 0) {
			return true;
		} else {
			return false;
		}
	}

	public String getChannelUrl() {
		return channelUrl_;
	}
	
	public String getChannelSUrl() {
		return channelSUrl_;
	}
	
	public String getChannelFunId() {
		return channelFunId_;
	}
	
	public String getChannelName() {
		return channelName_;
	}

	public void setChannelUrl(String url) {
		channelUrl_ = url;
	}
	
	public void setChannelSUrl(String url) {
		channelSUrl_ = url;
	}
	
	public void setChannelFunId(String id) {
		channelFunId_ = id;
	}
	
	public void setChannelName(String name) {
		channelName_ = name;
	}

	public String getIconName_() {
		return iconName_;
	}

	public void setIconName_(String iconName) {
		iconName_ = iconName;
	}
}
